package top.lishuoboy.hibernate.validator.config.advice;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import top.lishuoboy.hibernate.validator.dto.Result;
import top.lishuoboy.hibernate.validator.dto.ResultEnum;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Path;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * <h1>控制器统一异常处理
 * <p>
 * 2024-10-25
 *
 * @author lishuoboy
 */
@Slf4j
@RestControllerAdvice(basePackages = "top.lishuoboy.hibernate.validator.controller")
@RequiredArgsConstructor
public class ExceptionControllerAdvice {

    /** 任何异常（除下面异常以外） */
    @ResponseStatus             // 默认500
    @ExceptionHandler
    public Result exceptionHandler(Throwable t) {
        log.warn("异常", t);
        return Result.failure(t);
    }

    /** HV原生异常。非级联校验抛出的异常 */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler
    public Result<Map<String, List<String>>> constraintViolationExceptionHandler(ConstraintViolationException e) {
        log.error("参数校验异常", e);
        // 将异常平面结构转为按字段聚合的Map
        Map<String, List<String>> failFields = new LinkedHashMap<>();
        for (ConstraintViolation<?> cv : e.getConstraintViolations()) {
            Class<?> rootBeanClass = cv.getRootBeanClass();
            Path propertyPath = cv.getPropertyPath();
            Object invalidValue = cv.getInvalidValue();
            String message = cv.getMessage();
//            String key = StrUtil.format("{}=={}", propertyPath, invalidValue);
            String key = StrUtil.format("{}.{}=={}", rootBeanClass.getSimpleName(), propertyPath, invalidValue);
            if (failFields.containsKey(key)) {
                failFields.get(key).add(message);
            } else {
                failFields.put(key, ListUtil.list(false, message));
            }
        }
        return Result.failure(ResultEnum.invalidParameter, failFields);
    }

    /** SB参数异常。级联校验抛出的异常(MethodArgumentNotValidException的父类：BindException) */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler
    public Result<Map<String, List<String>>> bindExceptionHandler(BindException e) {
        log.error("参数校验异常", e);
        Map<String, List<String>> failFields = new LinkedHashMap<>();
        // 将异常平面结构转为按字段聚合的Map
        List<ObjectError> objectErrors = e.getBindingResult().getAllErrors();
        for (ObjectError objectError : objectErrors) {
            if (objectError instanceof FieldError fieldError) {
                String objectName = fieldError.getObjectName();
                String field = fieldError.getField();
                Object rejectedValue = fieldError.getRejectedValue();
                String defaultMessage = fieldError.getDefaultMessage();
//                String key = StrUtil.format("{}=={}", field, rejectedValue);
                String key = StrUtil.format("{}.{}=={}", objectName, field, rejectedValue);
                if (failFields.containsKey(key)) {
                    failFields.get(key).add(defaultMessage);
                } else {
                    failFields.put(key, ListUtil.list(false, defaultMessage));
                }
            } else {
                log.warn("不属于FieldError，可考虑优化。{}", objectError.getClass());
            }
        }
        return Result.failure(ResultEnum.invalidParameter, failFields);
    }

    /** SB @RequestParam 缺少参数异常 */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler
    public Result<Map<String, List<String>>> missingServletRequestParameterExceptionHandler(MissingServletRequestParameterException e) {
        log.error("缺少参数异常", e);
        Map<String, List<String>> failFields = new LinkedHashMap<>();
        String key = StrUtil.format("{}({})=={}", e.getParameterName(), e.getParameterType(), null);
        failFields.put(key, ListUtil.list(false, "不能为null"));
        return Result.failure(ResultEnum.missingParameter, failFields);
    }

    /** SB 参数类型不匹配异常 */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler
    public Result<Map<String, List<String>>> methodArgumentTypeMismatchExceptionHandler(MethodArgumentTypeMismatchException e) {
        log.error("参数类型不匹配异常", e);
        Map<String, List<String>> failFields = new LinkedHashMap<>();
        String type = e.getParameter().getParameterType().getSimpleName();
        String key = StrUtil.format("{}({})=={}", e.getName(), type, e.getValue());
        failFields.put(key, ListUtil.list(false, "参数类型不匹配"));
        return Result.failure(ResultEnum.parameterTypeMismatch, failFields);
    }
}
